package com.aizuda.easyManagerTool.service.setting.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import com.aizuda.easy.security.domain.Rep;
import com.aizuda.easy.security.domain.Req;
import com.aizuda.easyManagerTool.domain.bo.PackagesBO;
import com.aizuda.easyManagerTool.domain.dto.PageDTO;
import com.aizuda.easyManagerTool.domain.entity.setting.SettingRechargeRecordEntity;
import com.aizuda.easyManagerTool.domain.vo.PageVO;
import com.aizuda.easyManagerTool.domain.vo.setting.PackageVO;
import com.aizuda.easyManagerTool.domain.vo.setting.SettingUserVO;
import com.aizuda.easyManagerTool.mapper.setting.SettingRechargeRecordMapper;
import com.aizuda.easyManagerTool.service.setting.SettingRechargeService;
import com.aizuda.easyManagerTool.util.AssertUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.payments.nativepay.model.Amount;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse;
import com.wechat.pay.java.service.payments.nativepay.model.QueryOrderByOutTradeNoRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class SettingRechargeServiceImpl implements SettingRechargeService {

    @Resource
    private SettingRechargeRecordMapper settingRechargeRecordMapper;
    private List<PackagesBO> packages = new ArrayList<>();
    private Config config;

    /** 商户号 */
    private String merchantId = "1111";
    private String appId = "2222";

    {
        packages.add(new PackagesBO(
                1,"免费套餐",new BigDecimal(0),-1,
                Arrays.asList(
                        new PackagesBO.Item("服务器资源数量限制",10,"个",true,"server"),
                        new PackagesBO.Item("服务器多联终端",0,"",true,"remotePart"),
                        new PackagesBO.Item("邮件、钉钉等告警设置服务",0,"",false,"alarmNotice"),
                        new PackagesBO.Item("员工管理，可团队使用",0,"",false,"user"),
                        new PackagesBO.Item("7*24 小时技术支持",0,"",false,null)
                )
        ));
        packages.add(new PackagesBO(
                2,"月度套餐",new BigDecimal(12),1,
                Arrays.asList(
                        new PackagesBO.Item("服务器资源数量限制",2000,"个",true,"server"),
                        new PackagesBO.Item("服务器多联终端",0,"",true,"remotePart"),
                        new PackagesBO.Item("邮件、钉钉等告警设置服务",0,"",true,"alarmNotice"),
                        new PackagesBO.Item("员工管理，可团队使用",0,"",true,"user"),
                        new PackagesBO.Item("7*24 小时技术支持",0,"",true,null)
                )
        ));
        packages.add(new PackagesBO(
                3,"季度套餐",new BigDecimal(28.8),3,
                Arrays.asList(
                        new PackagesBO.Item("服务器资源数量限制",2000,"个",true,"server"),
                        new PackagesBO.Item("服务器多联终端",0,"",true,"remotePart"),
                        new PackagesBO.Item("邮件、钉钉等告警设置服务",0,"",true,"alarmNotice"),
                        new PackagesBO.Item("员工管理，可团队使用",0,"",true,"user"),
                        new PackagesBO.Item("7*24 小时技术支持",0,"",true,null)
                )
        ));
        packages.add(new PackagesBO(
                4,"半年套餐",new BigDecimal(50.5),6,
                Arrays.asList(
                        new PackagesBO.Item("服务器资源数量限制",2000,"个",true,"server"),
                        new PackagesBO.Item("服务器多联终端",0,"",true,"remotePart"),
                        new PackagesBO.Item("邮件、钉钉等告警设置服务",0,"",true,"alarmNotice"),
                        new PackagesBO.Item("员工管理，可团队使用",0,"",true,"user"),
                        new PackagesBO.Item("7*24 小时技术支持",0,"",true,null)
                )
        ));
        packages.add(new PackagesBO(
                5,"整年套餐",new BigDecimal(86.4),12,
                Arrays.asList(
                        new PackagesBO.Item("服务器资源数量限制",2000,"个",true,"server"),
                        new PackagesBO.Item("服务器多联终端",0,"",true,"remotePart"),
                        new PackagesBO.Item("邮件、钉钉等告警设置服务",0,"",true,"alarmNotice"),
                        new PackagesBO.Item("员工管理，可团队使用",0,"",true,"user"),
                        new PackagesBO.Item("7*24 小时技术支持",0,"",true,null)
                )
        ));
    }

    {
//        try {
//            // 解决 java.security.InvalidKeyException: Illegal key size
//            Security.setProperty("crypto.policy", "unlimited");
//            /** 商户证书序列号 */
//            String merchantSerialNumber = "33333";
//            /** 商户APIV3密钥 */
//            String apiV3Key = "444444";
//            ClassPathResource resource = new ClassPathResource("apiclient_key.pem");
//            String s1 = IOUtil.toString(resource.getStream());
//            /** 商户key私钥 */
//            PrivateKey privateKey = PemUtil.loadPrivateKeyFromString(s1);
//            this.config = new RSAAutoCertificateConfig.Builder()
//                            .merchantId(merchantId)
//                            .privateKey(privateKey)
//                            .merchantSerialNumber(merchantSerialNumber)
//                            .apiV3Key(apiV3Key)
//                            .build();
//        }catch (Exception e){
//            e.printStackTrace();
//        }
    }

    @Override
    public Rep<PageVO<SettingRechargeRecordEntity>> record(Req<PageDTO<SettingRechargeRecordEntity>, SettingUserVO> req) {
        PageDTO<SettingRechargeRecordEntity> pageDTO = req.getData();
        Page<SettingRechargeRecordEntity> page = new Page<SettingRechargeRecordEntity>(pageDTO.getCurrent(), pageDTO.getSize());
        SettingRechargeRecordEntity settingRechargeRecord = pageDTO.getData();
        settingRechargeRecord = ObjectUtil.isEmpty(settingRechargeRecord)? new SettingRechargeRecordEntity(): settingRechargeRecord;
        IPage<SettingRechargeRecordEntity> settingRechargeRecordEntityIPage  = settingRechargeRecordMapper.find(page, settingRechargeRecord);
        PageVO<SettingRechargeRecordEntity> pageVO = new PageVO<SettingRechargeRecordEntity>(pageDTO)
                .setTotal(settingRechargeRecordEntityIPage.getTotal())
                .setRecords(settingRechargeRecordEntityIPage.getRecords().stream().map(i -> decode(i)).collect(Collectors.toList()));
        return Rep.ok(pageVO);
    }

    @Override
    public Rep<String> pay(Req<PackagesBO, SettingUserVO> req) {
        PackagesBO data = req.getData();
        SettingUserVO user = req.getUser();
        String orderNumber = IdUtil.simpleUUID();
        // 请求支付接口
        String codeUrl = wxPay(orderNumber, data.getPrice(), data.getTitle());
        // 添加一个订单
        SettingRechargeRecordEntity settingRechargeRecord = new SettingRechargeRecordEntity();
        settingRechargeRecord.setOrderNumber(orderNumber);
        settingRechargeRecord.setPackageId(data.getId().toString());
        settingRechargeRecord.setPackageTitle(data.getTitle());
        settingRechargeRecord.setPackagePrice(data.getPrice().toString());
        settingRechargeRecord.setPackageMonth(data.getMonth().toString());
        settingRechargeRecord.setTenantId(user.getTenantId());
        settingRechargeRecord.setPackagePayResult("NOTPAY");
        settingRechargeRecordMapper.insert(encode(settingRechargeRecord));
        return Rep.ok(codeUrl);
    }

    @Override
    public Rep<PackageVO> packages(Req<Object, SettingUserVO> req) {
        SettingUserVO user = req.getUser();
        PackageVO packageVO = new PackageVO();
        packageVO.setPackagesBO(user.getPackageBO());
        packageVO.setList(packages);
        return Rep.ok(packageVO);
    }

    @Transactional
    @Override
    public Rep<SettingRechargeRecordEntity> refreshResults(Req<SettingRechargeRecordEntity, SettingUserVO> req) throws Exception {
        SettingRechargeRecordEntity data = req.getData();
        AssertUtil.objIsNull(data.getOrderNumber(), "数据错误");
        SettingRechargeRecordEntity byOrder = settingRechargeRecordMapper.findByOrder(data.getOrderNumber());
        byOrder = decode(byOrder);
        if("SUCCESS".equals(byOrder.getPackagePayResult())){
            return Rep.error(500,"支付成功，无需刷新");
        }
        Transaction transaction = queryOrders(data.getOrderNumber());
        if("SUCCESS".equals(transaction.getTradeState().name())){
            byOrder.setPackagePayResult(transaction.getTradeState().name());
            byOrder.setExpirationDate(LocalDate.now().plusDays(30).toString());
            byOrder = encode(byOrder);
            settingRechargeRecordMapper.updateByOrder(byOrder);
        }
        return Rep.ok(byOrder);
    }

    @Override
    public PackagesBO findPackage(Integer tenantId) {
        // 查询用户的套餐
        SettingRechargeRecordEntity data = settingRechargeRecordMapper.findUserPackage(tenantId);
        if(ObjectUtil.isEmpty(data)){
            return packages.get(0);
        }
        SettingRechargeRecordEntity finalData = decode(data);
        LocalDate parse = LocalDate.parse(finalData.getExpirationDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        if(parse.isBefore(LocalDate.now())){
            return packages.get(0);
        }
        return packages.stream().filter(i -> finalData.getPackageId().equals(i.getId().toString())).findAny().get();
    }

    @Override
    public Rep<SettingRechargeRecordEntity> delRecode(Req<SettingRechargeRecordEntity, SettingUserVO> req) {
        SettingRechargeRecordEntity data = req.getData();
        settingRechargeRecordMapper.deleteByOrderNumber(data.getOrderNumber());
        return Rep.ok();
    }

    private SettingRechargeRecordEntity decode(SettingRechargeRecordEntity data){
        String key = merchantId+data.getOrderNumber();
        AES aes = SecureUtil.aes(key.substring(0, 16).getBytes());
        if(ObjectUtil.isNotEmpty(data.getPackageTitle())) {
            data.setPackageTitle(aes.decryptStr(data.getPackageTitle()));
        }
        if(ObjectUtil.isNotEmpty(data.getPackageMonth())) {
            data.setPackageMonth(aes.decryptStr(data.getPackageMonth()));
        }
        if(ObjectUtil.isNotEmpty(data.getPackagePrice())) {
            data.setPackagePrice(aes.decryptStr(data.getPackagePrice()));
        }
        if(ObjectUtil.isNotEmpty(data.getExpirationDate())) {
            data.setExpirationDate(aes.decryptStr(data.getExpirationDate()));
        }
        if(ObjectUtil.isNotEmpty(data.getPackagePayResult())) {
            data.setPackagePayResult(aes.decryptStr(data.getPackagePayResult()));
        }
        if(ObjectUtil.isNotEmpty(data.getPackageId())) {
            data.setPackageId(aes.decryptStr(data.getPackageId()));
        }
        return data;
    }

    private SettingRechargeRecordEntity encode(SettingRechargeRecordEntity data){
        String key = merchantId+data.getOrderNumber();
        AES aes = SecureUtil.aes(key.substring(0, 16).getBytes());
        if(ObjectUtil.isNotEmpty(data.getPackageTitle())) {
            data.setPackageTitle(aes.encryptHex(data.getPackageTitle()));
        }
        if(ObjectUtil.isNotEmpty(data.getPackageMonth())) {
            data.setPackageMonth(aes.encryptHex(data.getPackageMonth()));
        }
        if(ObjectUtil.isNotEmpty(data.getPackagePrice())) {
            data.setPackagePrice(aes.encryptHex(data.getPackagePrice()));
        }
        if(ObjectUtil.isNotEmpty(data.getExpirationDate())) {
            data.setExpirationDate(aes.encryptHex(data.getExpirationDate()));
        }
        if(ObjectUtil.isNotEmpty(data.getPackagePayResult())) {
            data.setPackagePayResult(aes.encryptHex(data.getPackagePayResult()));
        }
        if(ObjectUtil.isNotEmpty(data.getPackageId())) {
            data.setPackageId(aes.encryptHex(data.getPackageId()));
        }
        return data;
    }


    /**
     * 得到二维码路径，需要自己生产二维码
     */
    private String wxPay(String merchantOrdersNumber, BigDecimal price,String title){
        // 构建service
        NativePayService service = new NativePayService.Builder().config(this.config).build();
        // request.setXxx(val)设置所需参数，具体参数可见Request定义
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        // 单位为 分
        amount.setTotal(price.multiply(BigDecimal.valueOf(100)).intValue());
        request.setAmount(amount);
        request.setAppid(appId);
        request.setMchid(merchantId);
        request.setDescription(title);
        request.setNotifyUrl("https://xianwate.com");
        request.setOutTradeNo(merchantOrdersNumber);
        request.setTimeExpire(LocalDateTime.now().plusMinutes(30).atOffset(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")).toString());
        // 调用下单方法，得到应答
        PrepayResponse response = service.prepay(request);
        // 使用微信扫描 code_url 对应的二维码，即可体验Native支付
        return response.getCodeUrl();
    }

    /**
     * 根据商家订单号查询
     */
    private Transaction queryOrders(String merchantOrdersNumber){
        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        request.setMchid(merchantId);
        request.setOutTradeNo(merchantOrdersNumber);
        NativePayService service = new NativePayService.Builder().config(this.config).build();
        return service.queryOrderByOutTradeNo(request);
    }


}
